﻿using System;
using System.Collections.Generic;
using System.Text;
using OpenGameEngine.Base;
using OpenGameEngine.Hud;
using OpenGameEngine.GameObjects;
using System.Collections.ObjectModel;
using OpenGameEngine.Input;
using swf = System.Windows.Forms;
using OpenGameEngine.Gui;

namespace OpenGameEngine.Scenes
{
    public abstract class GameScene
        : Object, IDisposable
    {
        public event EventHandler<GameCoreEventArgs> SceneEntered;
        public event EventHandler<GameCoreEventArgs> SceneEnded;

        private Boolean _mouseDown;

        public Boolean AcceptsMouseInput { get; set; }
        public Boolean AcceptsKeyboardInput { get; set; }

        #region IDisposable
        private Boolean _disposed;

        ~GameScene()
        {
            if (!_disposed)
            {
                Dispose(false);
                _disposed = true;
            }
        }

        public void Dispose()
        {
            if (!_disposed)
            {
                Dispose(true);
                _disposed = true;
            }

            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(Boolean disposeManagedResources)
        {
            if (disposeManagedResources)
            {
                if (Name != null)
                    Name = null;

                foreach (HudLayer hl in this.HudLayer)
                    hl.Dispose();
            }
        }
        #endregion

        public override bool Equals(object obj)
        {
            Boolean equal = base.Equals(obj);

            GameScene s = obj as GameScene;

            if (s != null)
            {
                if (s.Name == this.Name && s.Id == this.Id)
                    equal = true;
                else
                    equal = false;
            }

            return equal;
        }

        public override int GetHashCode()
        {
            Int32 lenght = Name.Length + 1;
            Int32 hLenght = HudLayer.Count + 1;
            Int32 goLenght = GameObjects.Count + 1;
            Int32 idHash = Id.GetHashCode() + 1;
            return (((base.GetHashCode() + idHash) ^ lenght) ^ hLenght) ^ goLenght;
        }

        /// <summary>
        /// The scene name
        /// </summary>
        public String Name { get; protected set; }
        /// <summary>
        /// Indicates if the scene is loaded
        /// </summary>
        public Boolean IsLoaded { get; protected set; }
        private Guid Id { get; set; }

        private Collection<HudLayer> _hudLayer;

        public Collection<HudLayer> HudLayer { get { return _hudLayer; } }

        private Collection<GameObject> _gameObjects;

        /// <summary>
        /// Game objects that will be handled 
        /// </summary>
        public Collection<GameObject> GameObjects { get { return _gameObjects; } }

        private Collection<Control> _controls;

        public Collection<Control> Controls { get { return _controls; } }

        /// <summary>
        /// Creates a new game scene
        /// </summary>
        protected GameScene(String name)
        {
            Id = Guid.NewGuid();
            Name = name;
            _hudLayer = new Collection<HudLayer>();
            _gameObjects = new Collection<GameObject>();
            _controls = new Collection<Control>();

            AcceptsMouseInput = true;
            AcceptsKeyboardInput = true;
        }

        internal protected virtual void OnSceneEntered(GameCoreEventArgs e)
        {
            if (SceneEntered != null)
                SceneEntered(this, e);
        }

        internal protected virtual void OnSceneEnded(GameCoreEventArgs e)
        {
            if (SceneEnded != null)
                SceneEnded(this, e);
        }

        /// <summary>
        /// Gets called when the game ends or the current scene got switched
        /// </summary>
        public virtual void Close(GameCoreEventArgs e)
        {

        }

        /// <summary>
        /// Loads the scene
        /// </summary>
        /// <param name="e">The load event args</param>
        public virtual void Load(GameCoreEventArgs e)
        {
            IsLoaded = true;
        }

        /// <summary>
        /// Draws the scene
        /// </summary>
        /// <param name="e">THe draw event args</param>
        public virtual void Draw(DrawEventArgs e)
        {
            foreach (GameObject o in this.GameObjects)
            {
                if (o != null)
                {
                    if (o.Visible && o.IsLoaded)
                        o.Draw(e);
                }
            }

            Control[] tbs = new Control[this.Controls.Count];
            this.Controls.CopyTo(tbs, 0);

            foreach (Control tb in tbs)
            {
                if (tb.Visible && tb.IsLoaded)
                    tb.Draw(e);
            }

            foreach (HudLayer l in this.HudLayer)
            {
                if (l.Visible)
                {
                    foreach (GameObject go in l.GetElements())
                    {
                        if (go.Visible && go.IsLoaded)
                            go.Draw(e);
                    }
                }
            }
        }

        private void CheckForInput(GameCoreEventArgs e)
        {
            if (AcceptsMouseInput)
            {
                MouseStateInfo prevMouse = e.Core.PreviousMouseStateInfo;
                MouseStateInfo mouse = e.Core.CurrentMouseStateInfo;

                foreach (swf.MouseButtons b in Globals.MouseButtons)
                {
                    bool blnCurrMs = mouse[b];
                    bool blnPrevMs = prevMouse[b];

                    if (blnCurrMs)
                    {
                        MouseDown(e, b);
                    }
                    else if (blnPrevMs && !blnCurrMs)
                    {
                        MouseUp(e, b);
                        MouseClick(e, b);
                    }
                }
            }


            if (AcceptsKeyboardInput)
            {
                KeyboardStateInfo kb = e.Core.CurrentKeyboardStateInfo;
                KeyboardStateInfo prevKb = e.Core.PreviousKeyboardStateInfo;

                foreach (swf.Keys key in Globals.KeyNames)
                {
                    bool blnCurrKb = kb[key];
                    bool blnPrevKb = prevKb[key];

                    if (blnCurrKb && !blnPrevKb)
                    {
                        KeyDown(e, key);
                    }
                    else if (blnPrevKb && !blnCurrKb)
                    {
                        KeyUp(e, key);
                    }
                }
            }
        }

        /// <summary>
        /// Updates the scene
        /// </summary>
        /// <param name="e">The update event args</param>
        public virtual void Update(GameCoreEventArgs e)
        {
            if (!IsLoaded)
                throw new InvalidOperationException("The scene isn't loaded yet");

            HudLayer[] ls = new HudLayer[this.HudLayer.Count];
            this.HudLayer.CopyTo(ls, 0);

            foreach (HudLayer l in ls)
            {
                if (l.Visible)
                {
                    GameObject[] gos2 = l.GetElements();

                    foreach (GameObject go in gos2)
                    {
                        if (!go.IsLoaded)
                            go.Load(e);

                        if (go.Enabled)
                            go.Update(e);
                    }
                }
            }

            Control[] tbs = new Control[this.Controls.Count];
            this.Controls.CopyTo(tbs, 0);

            foreach (Control tb in tbs)
            {
                if (!tb.IsLoaded)
                    tb.Load(e);

                if (tb.Enabled)
                    tb.Update(e);
            }

            GameObject[] gos = new GameObject[this.GameObjects.Count];
            this.GameObjects.CopyTo(gos, 0);

            foreach (GameObject o in gos)
            {
                if (o != null)
                {
                    if (!o.IsLoaded)
                        o.Load(e);

                    if (o.Enabled)
                        o.Update(e);
                }
            }

            CheckForInput(e);
        }

        public virtual void MouseDown(GameCoreEventArgs e, swf.MouseButtons button)
        {

        }

        public virtual void MouseUp(GameCoreEventArgs e, swf.MouseButtons button)
        {

        }

        public virtual void MouseClick(GameCoreEventArgs e, swf.MouseButtons button)
        {

        }

        public virtual void KeyDown(GameCoreEventArgs e, swf.Keys key)
        {

        }

        public virtual void KeyUp(GameCoreEventArgs e, swf.Keys key)
        {

        }
    }
}
